﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Text;
using System.Threading;
using Common.Utilities;
using Common.Utilities.Dispatcher;
using PdfLib;
using Workflows.Components.TextMining.PdfBookmarkExtraction;

namespace Workflows.Components.TextMining.TextScraping
{
    /// <summary>
    /// 
    /// </summary>
    public class PdfDocFullTextWorker:IWorker 
    {
        #region fields
        private Thread _WorkerThread;
        private PdfDocFullTextInstruction _Instruction;
        private PdfDocFullTextOutput _Output;
        private Guid _JobTicketID;
        private RunContext _Context;
        private string _WorkflowName;
        private string _ComponentName;

        /// <summary>
        /// 
        /// </summary>
        public string ComponentName
        {
            get { return _ComponentName; }
        }

        /// <summary>
        /// 
        /// </summary>
        public string WorkflowName
        {
            get { return _WorkflowName; }
        }

        #endregion

        #region IWorker Members

        public Thread WorkerThread
        {
            get { return _WorkerThread; }
        }

        public InstructionBase Instruction
        {
            get { return _Instruction; }
        }

        public ResultBase Output
        {
            get { return _Output; }
        }

        public Type ComponentType
        {
            get { return typeof(PdfDocFullTextComponent); }
        }

        public RunContext Context
        {
            get { return _Context; }
            set { _Context = value; }
        }

        public void SetInstruction(Guid ticketGuid, InstructionBase request, string workflowName, string componentName)
        {
            this._JobTicketID = ticketGuid;
            this._Instruction = (PdfDocFullTextInstruction)request;
            this._WorkflowName = workflowName;
            this._ComponentName = componentName;
        }

        public void SetInstruction(RunContext context, Guid ticketGuid, InstructionBase request, string workflowName, string componentName)
        {
            this._JobTicketID = ticketGuid;
            this._Instruction = (PdfDocFullTextInstruction)request;
            this._Context = context;
            this._WorkflowName = workflowName;
            this._ComponentName = componentName;
        }

        public void DoWork()
        {
            if (this.WorkerStartedEvent != null)
            {
                this.WorkerStartedEvent(this._JobTicketID, this, this.GetType(), this._Instruction);
            }
            this._WorkerThread = Thread.CurrentThread;
            this.ExtractPdfDocContent();
        }


        public void DoWorkAsync()
        {
            if (this.WorkerStartedEvent != null)
            {
                this.WorkerStartedEvent(this._JobTicketID, this, this.GetType(), this._Instruction);
            }
            this._WorkerThread = new Thread(new ThreadStart(this.ExtractPdfDocContent));
            this._WorkerThread.Start();
        }

        public void CancelWork()
        {
            if (this._WorkerThread != null && this._WorkerThread.IsAlive)
            {
                this._WorkerThread.Abort();
                if (this.WorkerCancelledEvent != null)
                {
                    this.WorkerCancelledEvent(this._JobTicketID, this.GetType(), this._Instruction);
                }
            }

        }

        public event WorkerStarted WorkerStartedEvent;
        public event WorkerFinished WorkerFinishedEvent;
        public event WorkerHasError WorkerErredEvent;
        public event WorkerCancelled WorkerCancelledEvent;
        public event WorkerStatusChanged WorkerStatusChangedEvent;

        #endregion

        #region work
        private void ExtractPdfDocContent()
        {
            SqlConnection srcConn = new SqlConnection(this._Instruction.SrcConnStr);
            SqlConnection tgtConn = new SqlConnection(this._Instruction.TgtConnStr);

            try
            {
                TableColumn[] cols =
                DbUtil.GetTableColumns(DataProviderType.MSSQL, this._Instruction.TgtConnStr,
                                       string.Empty, this._Instruction.TgtTableName);
                List<TableColumn> tgtCols = new List<TableColumn>();
                Dictionary<string, SqlParameter> parameters = new Dictionary<string, SqlParameter>();
                foreach (TableColumn col in cols)
                {
                    if (col.ColumnName.ToLower() == this._Instruction.TgtIDField.ToLower())
                    {
                        tgtCols.Add(col);
                        parameters.Add(this._Instruction.TgtIDField, new SqlParameter("@" + col.ColumnName, DBNull.Value));
                    }
                    else if (col.ColumnName.ToLower() == this._Instruction.FullTextField.ToLower())
                    {
                        tgtCols.Add(col);
                        parameters.Add(this._Instruction.FullTextField,
                                       new SqlParameter("@" + col.ColumnName, DBNull.Value));
                    }
                    else if (col.ColumnName.ToLower() == this._Instruction.FlagField.ToLower())
                    {
                        tgtCols.Add(col);
                        parameters.Add(this._Instruction.FlagField,
                                       new SqlParameter("@" + col.ColumnName, DBNull.Value));
                    }
                    else if (col.ColumnName.ToLower() == this._Instruction.UpdField.ToLower())
                    {
                        tgtCols.Add(col);
                        parameters.Add(this._Instruction.UpdField,
                                       new SqlParameter("@" + col.ColumnName, DBNull.Value));
                    }
                }
                string spName = "bulkInsert_" + this._Instruction.TgtTableName + "_SP";
                string spBody = StoredProcUtil.CreateStoredProcedureStatementForInsert(
                    DataProviderType.MSSQL, spName, string.Empty, this._Instruction.TgtTableName,
                    tgtCols.ToArray());
                DbUtil.NewStoredProc(this._Instruction.TgtConnStr, spName, spBody);
                int totalSrcRecordCount = DbUtil.GetTableRecordCount(
                    DataProviderType.MSSQL, this._Instruction.SrcConnStr, this._Instruction.SelectSql);

                srcConn.Open();
                SqlCommand srcCmd = srcConn.CreateCommand();
                srcCmd.CommandType = CommandType.Text;
                srcCmd.CommandText = this._Instruction.SelectSql;
                SqlDataReader reader = srcCmd.ExecuteReader();
                tgtConn.Open();
                SqlCommand tgtCmd = tgtConn.CreateCommand();
                tgtCmd.CommandType = CommandType.Text;
                tgtCmd.CommandText = string.Format("select {0} from {1} where {2}={3}",
                                                   this._Instruction.TgtIDField,
                                                   this._Instruction.TgtTableName,
                                                   this._Instruction.FlagField,
                                                   (int)BookmarkExtractionFlag.Successful);
                SqlDataReader tgtReader = tgtCmd.ExecuteReader();
                List<string> existingKeys=new List<string>();
                while (tgtReader.Read())
                {
                    object existingKey = tgtReader[0];
                    if (existingKey !=null && !existingKeys.Contains(existingKey.ToString().ToUpper()))
                    {
                        existingKeys.Add(existingKey.ToString().ToUpper());
                    }
                }
                tgtReader.Close();
                tgtCmd.CommandType = CommandType.StoredProcedure;
                tgtCmd.CommandText = spName;

                int fileProcessed = 0;
                int contentExtracted = 0;
                while (reader.Read())
                {
                    try
                    {
                        if (reader[this._Instruction.SrcIDField] != null && reader[this._Instruction.FilePathField] != null)
                        {
                            object id = reader[this._Instruction.SrcIDField];
                            if (existingKeys.Contains(id.ToString().ToUpper()))
                            {
                                fileProcessed++;
                                continue;
                            }
                            tgtCmd.Parameters.Clear();
                            foreach(string key in parameters.Keys)
                            {
                                parameters[key].Value = DBNull.Value;
                            }
                            BookmarkExtractionFlag flag = BookmarkExtractionFlag.Unknown;
                            parameters[this._Instruction.TgtIDField].Value = id;
                            
                            string filePath = reader[this._Instruction.FilePathField].ToString();
                            if(!string.IsNullOrEmpty(filePath) && File.Exists(filePath))
                            {
                                StringBuilder buffer = new StringBuilder();
                                try
                                {
                                    int pageCount = PdfPropertyReader.GetPageCount(filePath);
                                    for (int pageNum = 1; pageNum <= pageCount; pageNum++)
                                    {
                                        string pageContent = PdfTextReader.ReadPageContent(filePath, pageNum);
                                        buffer.Append(pageContent);
                                        buffer.AppendLine();
                                    }
                                    parameters[this._Instruction.FullTextField].Value = buffer.ToString();

                                    if (buffer.Length > 0)
                                    {
                                        contentExtracted += 1;
                                        flag = BookmarkExtractionFlag.Successful;
                                    }
                                    else
                                    {
                                        flag = BookmarkExtractionFlag.BookmarkContentNotAvailable;
                                    }
                                }
                                catch (Exception ex1)
                                {
                                    if (this.WorkerErredEvent != null)
                                    {
                                        this.WorkerErredEvent(this._JobTicketID, this.GetType(), this._Instruction,
                                                              ex1.ToString(), true);
                                    }
                                    flag = BookmarkExtractionFlag.BookmarkContentNotAvailable;
                                }
                            }
                            else
                            {
                                flag = BookmarkExtractionFlag.FileError;
                            }
                            if (parameters.ContainsKey(this._Instruction.FlagField))
                                parameters[this._Instruction.FlagField].Value = (int)flag;
                            if(!string.IsNullOrEmpty(this._Instruction.UpdField) && 
                                parameters.ContainsKey(this._Instruction.UpdField))
                                parameters[this._Instruction.UpdField].Value = DateTime.Now;
                            
                            foreach (SqlParameter parameter in parameters.Values)
                                tgtCmd.Parameters.Add(parameter);
                            tgtCmd.ExecuteNonQuery();
                        }
                        

                    }
                    catch (Exception ex1)
                    {
                        if (this.WorkerErredEvent != null)
                            this.WorkerErredEvent(this._JobTicketID, this.GetType(), this._Instruction, ex1.ToString(),true);
                    }

                    fileProcessed++;
                    if (this.WorkerStatusChangedEvent != null)
                    {
                        int pct = fileProcessed * 100 / totalSrcRecordCount;
                        string msg = string.Format("Processed {0} of {1} files", fileProcessed, totalSrcRecordCount);
                        this.WorkerStatusChangedEvent(this._JobTicketID, this.GetType(), this._Instruction, this.WorkflowName, this.ComponentName, msg, pct);
                    }
                }
                reader.Close();

                this._Output = new PdfDocFullTextOutput(
                    this._Instruction.OwnerComponentID, this._JobTicketID, fileProcessed, contentExtracted);

                if (this.WorkerFinishedEvent != null)
                {
                    this.WorkerFinishedEvent(this._JobTicketID, this.GetType(), this._Instruction, this._Output);
                }
            }
            catch (Exception ex)
            {
                if (this.WorkerErredEvent != null)
                {
                    this.WorkerErredEvent(this._JobTicketID, this.GetType(), this._Instruction, ex.ToString(),true);
                }
            }
            finally
            {
                srcConn.Close();
                tgtConn.Close();
            }
        }
        #endregion
    }
}
